/** @file    ObjectFactory.cpp
 *  @author  Alessandro Polo
 *  @version $Id: ObjectFactory.cpp 2832 2010-08-02 22:53:46Z alex $
 *  @brief
 * File containing methods for the wosh::ObjectFactory class.
 * The header for this class can be found in ObjectFactory.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <core/ObjectFactory.h>
 #include <core/WoshKernel.h>
 #include <core/SystemError.h>
 #include <core/MethodsCommon.h>
 #include <core/Table.h>
 #include <core/List.h>
 #include <core/Functional.h>
 #include <core/Utilities.h>
 #include <core/DataModels.h>


using namespace std;
namespace wosh {

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Meyer singleton: Instantiated on first use. BUT destroyed BY KERNEL! (here compiler will delete only the pointer)
extern "C" {
	ObjectFactory& ObjectFactory::getInstance() { 
		static ObjectFactory* instance_ObjectFactory = new ObjectFactory( WoshKernel::getInstance() );
		return *instance_ObjectFactory;
	}
}

// Meyer singleton: Instantiated on first use. Guaranteed to be destroyed.
extern "C" {
	StaticAllocator& StaticAllocator::getInstance() {
		static StaticAllocator instance_StaticAllocator;
		return instance_StaticAllocator;
	}
}
 // register as abstract because it is a singleton:
 WOSH_REGISTER_INTERFACE(wosh::ObjectFactory, "wosh::WoshModule", 0.01, _static_ObjectFactory )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

ObjectFactory::ObjectFactory( const WoshKernel* parent )
	: WoshModule(parent, _ObjectFactory_NAME, _ObjectFactory_URI) {

//Log.setLevel( LOG_VERBOSE );

	Log(LOG_DEBUG, " Acquiring StaticAllocator instance.." );
	ObjectAllocator::getInstances().push_back( &StaticAllocator::getInstance() );

	Log(LOG_DEBUG, " Initializing Properties.." );
	Properties.set( _ObjectFactory_KEY_Count, 0, Permission( Permission::Read ) );

	Log(LOG_DEBUG, " Registering Methods.." );
	MethodWRESULT* mmDump = Methods.create<MethodWRESULT>( "dump", "Get ", Permission::R_XR_XR__() );
	mmDump->setMethod( this, (MethodWRESULTPtr)&ObjectFactory::mmDoDump);

	MethodRequest* mmRemoveAllocator = Methods.create<MethodRequest>( _ObjectFactory_METHOD_remove_allocator, "remove allocator", Permission::R_XR_XR__() );
	mmRemoveAllocator->setMethod( this, (MethodRequestPtr)&ObjectFactory::mmDoRemoveAllocator );

	MethodRequest* mmGetTypesTree = Methods.create<MethodRequest>( _SystemMonitor_METHOD_types_tree, "Get RTTI tree", Permission::R_XR_XR__() );
	mmGetTypesTree->setMethod( this, (MethodRequestPtr)&ObjectFactory::mmDoGetTypesTree );

	MethodRequest* mmSupportsType = Methods.create<MethodRequest>( _SystemMonitor_METHOD_supports_type, "check if type is allocable", Permission(Permission::RX) );
	mmSupportsType->setMethod( this, (MethodRequestPtr)&ObjectFactory::mmDoSupportsType );

	MethodRequest* mmFindAllocator = Methods.create<MethodRequest>( _SystemMonitor_METHOD_find_allocator, "find allocator for a type", Permission(Permission::RX) );
	mmFindAllocator->setMethod( this, (MethodRequestPtr)&ObjectFactory::mmDoFindAllocator );

	MethodList* mmListAllocators = Methods.create<MethodList>( _METHOD_List, "Get Allocators", Permission::R_XR_XR__() );
	mmListAllocators->setDataFieldExtractor( DataFieldExtractor<ObjectAllocator>::createFor< DataAdapterListT<ObjectAllocator> >(ObjectAllocator::getInstances(), "Allocators") );
/*
	DataAdapterListT<ObjectAllocator>* dataAdapter = new DataAdapterListT<ObjectAllocator>( ObjectAllocator::getInstances() );
	DataFieldCollector<ObjectAllocator>* fields = new DataFieldCollector<ObjectAllocator>();
	fields->add<const std::string&, Variant>(&ObjectAllocator::getName, "Name", 0);
	fields->add<const char*, Variant>(&ObjectAllocator::getClassName, "Type", 1);
	DataFieldSubWrapperT<ObjectAllocator, const ObjectAllocator::tIReflectionList&, int, Variant>* dfwAllocatedCountInt;
	dfwAllocatedCountInt = new DataFieldSubWrapperT<ObjectAllocator, const ObjectAllocator::tIReflectionList&, int, Variant>(&ObjectAllocator::getAllocatedObjects, &ObjectAllocator::tIReflectionList::count, "#Allocated", 2);
	fields->add(dfwAllocatedCountInt);
	DataFieldSubWrapperT<ObjectAllocator, const ObjectAllocator::tStrObjectTypeNodeMap&, int, Variant>* dfwTypesCountInt;
	dfwTypesCountInt = new DataFieldSubWrapperT<ObjectAllocator, const ObjectAllocator::tStrObjectTypeNodeMap&, int, Variant>(&ObjectAllocator::getObjectsInfo, &ObjectAllocator::tStrObjectTypeNodeMap::count, "#Types", 3);
	fields->add(dfwTypesCountInt);
	fields->add<const std::string&,Variant>(&ObjectAllocator::getEntityID, "EntityID", 4, false);
	fields->add<const Permission&,Permission>(&ObjectAllocator::getPermission, "Permission", 5, false);
	DataFieldExtractor<ObjectAllocator>* dExt = new DataFieldExtractor<ObjectAllocator>(dataAdapter, fields);
	MethodList* mmListTMP = Methods.create<MethodList>( _METHOD_List, "Get Allocators", Permission::R_XR_XR__() );
	mmListTMP->setDataFieldExtractor(dExt);
*/
 }

ObjectFactory::~ObjectFactory() {
	Log(LOG_DEBUG, ":~ObjectFactory() Destroying.." );

	// it is released by dynamic de-initializer!
	//ObjectAllocator::getInstances().pop( &StaticAllocator::getInstance() );

	Log(LOG_VERBOSE, " Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS

WRESULT ObjectFactory::onKernelInitializing() {
	WRESULT ret = WoshModule::onKernelInitializing();
	// fix URI of allocators
	tObjectAllocatorList::Iterator it;
	const tObjectAllocatorList::ConstIterator it_end = ObjectAllocator::getInstances().end();
	for ( it=ObjectAllocator::getInstances().begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		(*it)->getURI().setParent( this->getURI() );
		(*it)->getURI().setName( (*it)->getName() );
		(*it)->getURI().registerGlobal();
	 }
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////// SETTINGS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

IReflection* ObjectFactory::createTypeOf( const std::string& className, double version_pattern, bool strict ) {
	if ( ObjectFactory::getInstance().Log.isDebug() )
		ObjectFactory::getInstance().Log(LOG_DEBUG, ":createTypeOf(%s [%f]) : Searching Allocators..", className.c_str(), version_pattern );
	ObjectAllocator::getInstances().transactionBeginRead();
	tObjectAllocatorList::ConstIterator it;
	const tObjectAllocatorList::ConstIterator it_end = ObjectAllocator::getInstances().end();
	for ( it=ObjectAllocator::getInstances().begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( ObjectFactory::getInstance().Log.isVerbose() )
			ObjectFactory::getInstance().Log(LOG_VERBOSE, ":createTypeOf(%s [%f]) : Found Allocator %s..", className.c_str(), version_pattern, (*it)->getName().c_str() );
		IReflection* object = (*it)->createTypeOf(className, version_pattern, strict);
		if ( object != NULL ) {
			ObjectAllocator::getInstances().transactionEnd();
			return object;
		 }
	 }
	ObjectAllocator::getInstances().transactionEnd();
	ObjectFactory::getInstance().Log(LOG_CRITICAL, ":createTypeOf(%s [%f]) : No Allocators found!", className.c_str(), version_pattern );
	return NULL;
 }

WRESULT ObjectFactory::destroy( IReflection* object ) {
	if ( object == NULL ) {
		ObjectFactory::getInstance().Log(LOG_WARNING, ":destroy(NULL)" );
		return WRET_ERR_PARAM;
	 }
	WRESULT ret = WRET_OK;
	if ( ObjectFactory::getInstance().Log.isDebug() )
		ObjectFactory::getInstance().Log(LOG_DEBUG, ":destroy(%p) Type %s", object, object->getClassName() );
	// find allocator of the object
	ObjectAllocator::getInstances().transactionBeginRead();
	ObjectAllocator* allocator = findAllocatorOf_( object );
	if ( allocator == NULL ) {
		ObjectAllocator::getInstances().transactionEnd();
		ObjectFactory::getInstance().Log(LOG_WARNING, ":destroy(%p) : Allocator missing! [%s]", object, object->getClassName() );
		return WRET_ERR_PARAM;
	 }
	// forward call
	if ( ObjectFactory::getInstance().Log.isVerbose() )
		ObjectFactory::getInstance().Log(LOG_VERBOSE, ":destroy(%p) Forwarding to  [%s]", object, allocator->getName().c_str(), object->getClassName() );
	ret = allocator->destroy( object );
	ObjectAllocator::getInstances().transactionEnd();
	return ret;
 }

bool ObjectFactory::isAllocable( const std::string& className, double version_pattern ) {
	ObjectAllocator::getInstances().transactionBeginRead();
	tObjectAllocatorList::ConstIterator it;
	const tObjectAllocatorList::ConstIterator it_end = ObjectAllocator::getInstances().end();
	for ( it=ObjectAllocator::getInstances().begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( !(*it)->isSupported(className, version_pattern, true) ) continue;
		ObjectAllocator::getInstances().transactionEnd();
		return true;
	 }
	ObjectAllocator::getInstances().transactionEnd();
	return false;
 }

WRESULT ObjectFactory::gatherInfoOf( std::vector<ObjectTypeInfo>& parent_list, const std::string& className, double version_pattern ) {
	ObjectAllocator::getInstances().transactionBeginRead();
	ObjectAllocator* allocator = findAllocatorOf_( className, version_pattern, false );
	if ( allocator == NULL ) {
		ObjectAllocator::getInstances().transactionEnd();
		ObjectFactory::getInstance().Log(LOG_WARNING, ":gatherInfoOf(%s,%g,[false]) : Allocator not found!", className.c_str(), version_pattern );
		return WRET_ERR_PARAM;
	 }
	WRESULT ret = allocator->gatherInfoOf( parent_list, className, version_pattern );
	ObjectAllocator::getInstances().transactionEnd();
	return ret;
 }

WRESULT ObjectFactory::findChildsOf( std::vector<ObjectTypeInfo>& parent_list, const std::string& className, double version_pattern, bool allocable ) {
	WRESULT ret = WRET_OK;
	ObjectAllocator::getInstances().transactionBeginRead();
	tObjectAllocatorList::ConstIterator it;
	const tObjectAllocatorList::ConstIterator it_end = ObjectAllocator::getInstances().end();
	for ( it=ObjectAllocator::getInstances().begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( !(*it)->isSupported(className, version_pattern, false) ) continue;
		ret += (*it)->findChildsOf( parent_list, className, version_pattern, allocable );
	 }
	ObjectAllocator::getInstances().transactionEnd();
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////// THREADs OPERATIONS

WRESULT ObjectFactory::installAllocator( ObjectAllocator* allocator ) {
	if ( allocator == NULL ) {
		Log(LOG_WARNING, ":installAllocator(NULL)" );
		return WRET_ERR_PARAM;
	 }
	WRESULT ret = WRET_OK;
	Log(LOG_VERBOSE, ":installAllocator(%p) Type %s", allocator, allocator->getName().c_str() );
	ObjectAllocator::getInstances().transactionBeginWrite();
	ObjectAllocator::getInstances().push_back(allocator);
	ObjectAllocator::getInstances().transactionEnd();
	return ret;
 }

WRESULT ObjectFactory::removeAllocator( ObjectAllocator* allocator ) {
	if ( allocator == NULL ) {
		Log(LOG_WARNING, ":removeAllocator(NULL)" );
		return WRET_ERR_PARAM;
	 }
	Log(LOG_VERBOSE, ":removeAllocator(%p) Type %s", allocator, allocator->getName().c_str() );
	ObjectAllocator::getInstances().transactionBeginWrite();
	bool removed = ObjectAllocator::getInstances().pop(allocator);
	ObjectAllocator::getInstances().transactionEnd();
	if ( removed )
		return WRET_OK;
	return WRET_ERR_PARAM;
 }

WRESULT ObjectFactory::findAllocatorOf( std::string& allocator, const std::string& className, double version_pattern, bool allocable ) {
	ObjectAllocator::getInstances().transactionBeginRead();
	tObjectAllocatorList::ConstIterator it;
	const tObjectAllocatorList::ConstIterator it_end = ObjectAllocator::getInstances().end();
	for ( it=ObjectAllocator::getInstances().begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( !(*it)->isSupported(className, version_pattern, allocable) ) continue;
		allocator = (*it)->getName();
		ObjectAllocator::getInstances().transactionEnd();
		return WRET_OK;
	 }
	ObjectAllocator::getInstances().transactionEnd();
	return WRET_ERR_PARAM;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

ObjectAllocator* ObjectFactory::findAllocatorOf_( IReflection* object ) {
	tObjectAllocatorList::ConstIterator it;
	const tObjectAllocatorList::ConstIterator it_end = ObjectAllocator::getInstances().end();
	for ( it=ObjectAllocator::getInstances().begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( !(*it)->allocatedObjects.exists(object) ) continue;
		return *it;
	 }
	ObjectFactory::getInstance().Log(LOG_WARNING, ":findAllocatorOf_(%p) Type %s", object, object->getClassName() );
	return NULL;
 }

ObjectAllocator* ObjectFactory::findAllocatorOf_( const std::string& className, double version_pattern, bool allocable ) {
	tObjectAllocatorList::ConstIterator it;
	const tObjectAllocatorList::ConstIterator it_end = ObjectAllocator::getInstances().end();
	for ( it=ObjectAllocator::getInstances().begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( !(*it)->isSupported(className, version_pattern, allocable) ) continue;
		return *it;
	 }
	return NULL;
 }

////////////////////////////////////////////////////////////////////////////////////////// THREADs OPERATIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS

Response* ObjectFactory::mmDoRemoveAllocator( const Request* request ) {
	if ( request == NULL ) return NULL;
	string all_name = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoRemoveAllocator(%s)", all_name.c_str() );

	ObjectAllocator::getInstances().transactionBeginWrite();
	tObjectAllocatorList::Iterator it;
	const tObjectAllocatorList::ConstIterator it_end = ObjectAllocator::getInstances().end();
	for ( it=ObjectAllocator::getInstances().begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( (*it)->getName() != all_name ) continue;
		if ( (*it) == &StaticAllocator::getInstance() ) {
			ObjectAllocator::getInstances().transactionEnd();
			return request->replyResponse(WRET_ERR_ILLEGAL_USE, "Static allocator can't be removed");
		 }
		ObjectAllocator::getInstances().erase(it);
		return request->replyResponse(WRET_OK, "Allocator removed and destroyed");
	 }
	ObjectAllocator::getInstances().transactionEnd();

	return request->replyResponse(WRET_ERR_PARAM, "allocator not found!");
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* ObjectFactory::mmDoGetTypesTree( const Request* request ) {
	if ( request == NULL ) return NULL;
	Variant vAllocatorName;
	Variant vTreeMode;
	request->extractArguments(vAllocatorName, vTreeMode);
	Log(LOG_VERBOSE, ":mmDoGetTypesTree(%s,%s)", vAllocatorName.toString().c_str(), vTreeMode.toString().c_str() );

	Tree* objTree = new Tree();
	if ( !vAllocatorName.isStringNotEmpty() )
		objTree->setTreeName("All Types" );
	else
		objTree->setTreeName("Types of " + vAllocatorName.asString());
	objTree->setRoot( new TreeNode(NULL) );
	ObjectAllocator::getInstances().transactionBeginRead();
	tObjectAllocatorList::ConstIterator it;
	const tObjectAllocatorList::ConstIterator it_end = ObjectAllocator::getInstances().end();
	for ( it=ObjectAllocator::getInstances().begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( vAllocatorName.isStringNotEmpty() && vAllocatorName.asString() != (*it)->getName() ) continue;
		(*it)->mergeTree_(*objTree, vTreeMode.toBoolean(true));
	 }
	ObjectAllocator::getInstances().transactionEnd();
	return request->replyResponse(WRET_OK, objTree);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ObjectFactory::mmDoDump() {
	BuiltInAllocator.getInstance().dump();
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* ObjectFactory::mmDoSupportsType( const Request* request ) {
	if ( request == NULL ) return NULL;
	string className = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoListAllocators(%s)", className.c_str() );
	bool yes = isAllocable( className );
	if ( yes )
		return request->replyResponse(WRET_OK, "supported!");
	return request->replyResponse(WRET_ERR_PARAM, "Not supported!");
}

Response* ObjectFactory::mmDoFindAllocator( const Request* request ) {
	if ( request == NULL ) return NULL;
	string className = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoFindAllocator(%s)", className.c_str() );
	std::string allocator;
	WRESULT ret = findAllocatorOf( allocator, className );
	if ( WFAILED(ret) ) 
		return request->replyResponse(WRET_ERR_PARAM, "Not supported!");
	return request->replyResponse(WRET_OK, new Variant(allocator));
	
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////


}; // namespace wosh
